<template>
	<view class="flex flex-col">
		<view v-if="model == 'line'"
			:style="[{ width: width + 'rpx', paddingTop: `${linePadding}rpx`, paddingBottom: `${linePadding}rpx` }]"
			class="flex relative flex flex-col overflow">
			<view class="relative">
				<tm-sheet no-level :round="props.round" :followTheme="false" :dark="props.dark" :margin="[0, 0]"
					:width="props.width" :height="props.height" :color="props.bgColor" :padding="[0, 0]">
				</tm-sheet>
			</view>
			<view class="absolute l-0 tmRogress overflow"
				:style="[{ width: activeWidth + 'rpx', top: `${linePadding}rpx` }]">
				<tm-sheet :round="props.round" :followTheme="props.followTheme" :dark="props.dark" :margin="[0, 0]"
					:linear="props.linear" :linearDeep="props.linearDeep" :width="activeWidth" :height="props.height"
					:color="props.color" :padding="[0, 0]">
				</tm-sheet>
			</view>
			<view v-if="props.showBar" class="absolute l-0 t-0 tmRogress flex flex-col" :style="[
				{
					width: activeWidth + 'rpx',
					height: props.height + 32 + 'rpx',
					'align-items': 'flex-end',
					'justify-content': 'center'
				}
			]">
				<slot>
					<tm-sheet :linear="props.linear" :linearDeep="props.linearDeep" :followTheme="props.followTheme"
						:dark="props.dark" :color="props.color" :margin="[0, 0]" :padding="[12, 4]" :round="4">
						<tm-text _class="text-size-xxs" :fontSize="22" :label="props.percent + props.percentSuffix">
						</tm-text>
					</tm-sheet>
				</slot>
			</view>
		</view>
		<view v-if="model == 'circle'" :style="{
			width: `${props.width}rpx`,
			height: `${props.semicircle ? props.width / 2 : props.width}rpx`
		}" class="flex relative flex-col">
			<!-- #ifdef APP-NVUE -->
			<view v-if="showGc">
				<gcanvas :id="canvasId" :ref="canvasId" class="canvas" :style="{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : props.width}rpx`
				}">
				</gcanvas>
			</view>
			<!-- #endif -->
			<!-- #ifdef MP-WEIXIN  || MP-QQ -->
			<canvas type="2d" id="canvasId" canvas-id="canvasId" class="canvas" :style="{
				width: `${props.width}rpx`,
				height: `${props.semicircle ? props.width / 2 : width}rpx`
			}"></canvas>
			<!-- #endif -->
			<!-- #ifdef MP-ALIPAY -->
			<canvas type="2d" :id="canvasId" :canvas-id="canvasId" class="canvas" :style="{
				width: `${props.width}rpx`,
				height: `${props.semicircle ? props.width / 2 : width}rpx`
			}"></canvas>
			<!-- #endif -->
			<!-- #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ || APP-NVUE -->
			<canvas :id="canvasId" :canvas-id="canvasId" class="canvas" :style="{
				width: `${props.width}rpx`,
				height: `${props.semicircle ? props.width / 2 : props.width}rpx`
			}"></canvas>
			<!-- #endif -->
			<!-- #ifndef APP-NVUE -->
			<cover-view :style="[
				{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : props.width}rpx`
				},
				props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
				props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : ''
			]" class="relative absolute l-0 t-0 flex flex-col" :class="[!props.semicircle ? 'flex-center' : '']">
				<cover-view v-if="props.showBar"
					:style="[{ fontSize: props.fontSize + 'rpx', color: isDark ? darkcolor : txtcolor }]">
					<slot name="title">
						{{ props.percent + props.percentSuffix }}
					</slot>
				</cover-view>
			</cover-view>
			<!-- #endif -->
			<!-- #ifdef APP-NVUE -->
			<cover-view :style="[
				{
					width: `${props.width}rpx`,
					height: `${props.semicircle ? props.width / 2 : props.width}rpx`
				},
				props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
				props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : ''
			]" class="relative absolute l-0 t-0 flex flex-col" :class="[!props.semicircle ? 'flex-center' : '']">
				<slot name="title">
					<tm-text v-if="props.showBar" :color="props.color" :followTheme="props.followTheme"
						:dark="props.dark" :fontSize="props.fontSize" :label="props.percent + props.percentSuffix">
					</tm-text>
				</slot>
			</cover-view>
			<!-- #endif -->
		</view>
	</view>
</template>

<script lang="ts" setup>
	/**
	 * 进度条
	 * @description 进度条，圆形进度条，在不同的平台使用2d或者webgl方法，使得性能更加强劲。NVUE中貌似是uniapp的插件bug无法实现渐变绘制。
	 * Nvue中需要在manifest.json中设置canvas模块，才能打包详见：https://github.com/dcloudio/NvueCanvasDemo
	 */
	import { cssstyle, tmVuetify, colorThemeType } from '../../tool/lib/interface'
	import { custom_props, computedDark, computedTheme } from '../../tool/lib/minxs'
	import { getCurrentInstance, computed, ref, provide, inject, onBeforeMount, onMounted, onUnmounted, nextTick, watch, PropType } from 'vue'
	import tmSheet from '../tm-sheet/tm-sheet.vue'
	import tmText from '../tm-text/tm-text.vue'
	import tool from '../../tool/theme/theme'
	import { useTmpiniaStore } from '../../tool/lib/tmpinia'
	import { getCanvas } from '../../tool/function/getCanvas'
	import * as TWEEN from '../../tool/lib/tween.min.js'
	// #ifdef APP-NVUE
	import { enable, WeexBridge } from '../../tool/gcanvas/index.js'
	// #endif
	const store = useTmpiniaStore()
	const emits = defineEmits(['update:percent', 'change'])
	const proxy = getCurrentInstance()?.proxy ?? null
	const props = defineProps({
		...custom_props,
		model: {
			type: String as PropType<'line' | 'circle'>,
			default: 'line' //line,circle
		},
		//model==circle,是否是半圆。
		semicircle: {
			type: [Boolean, String],
			default: false
		},
		//model==circle有效,半圆正常是在上方。如果反转就在下方。
		semicircleFlip: {
			type: [Boolean],
			default: false
		},
		//model==circle有效
		fontSize: {
			type: [Number, String],
			default: 28
		},
		//进度 百分比数值，不带%号。也可以使用v-model:percent
		percent: {
			type: Number,
			default: 0
		},
		//数值后缀。默认为%
		percentSuffix: {
			type: String,
			default: '%'
		},
		width: {
			type: Number,
			default: 120
		},
		height: {
			type: Number,
			default: 6
		},
		bgColor: {
			type: String,
			default: 'grey-4'
		},
		color: {
			type: String,
			default: 'primary'
		},
		//是否跟随全局主题的变换而变换
		followTheme: {
			type: [Boolean, String],
			default: true
		},
		//暗黑
		dark: {
			type: [Boolean],
			default: false
		},
		linear: {
			type: [String],
			default: '' //left:右->左，right:左->右。top:下->上，bottom:上->下。
		},
		// 渐变的亮浅
		linearDeep: {
			type: [String],
			default: 'light' //light,dark,accent亮系渐变和深色渐变。
		},
		round: {
			type: [Number, String],
			default: 3
		},
		//显示数值点
		showBar: {
			type: [Boolean, String],
			default: false
		},
		disabled: {
			type: Boolean,
			default: false
		},
		linePadding: {
			type: Number,
			default: 16
		}
	})
	const canvasId = ref('canvasId')
	// #ifndef MP-WEIXIN || MP-QQ
	canvasId.value = 'tm' + String(new Date().getTime())
	// #endif
	let ctx: UniApp.CanvasContext
	let reqId: number
	let tween: any
	const shadow_pr = computed(() => props.shadow * 4)
	// 设置响应式全局组件库配置表。
	const tmcfg = computed<tmVuetify>(() => store.tmStore)
	//是否暗黑模式。
	const isDark = computed(() => computedDark(props, tmcfg.value))
	//计算主题
	const tmcomputed = computed<cssstyle>(() => computedTheme(props, isDark.value, tmcfg.value))
	const showGc = ref(false)
	const sys = uni.getSystemInfoSync()
	let isAndroid = false
	// #ifdef APP-NVUE
	if (sys.osName == 'android') {
		isAndroid = true
	}
	// #endif
	const _bgColor = computed(
		() => computedTheme({ ...props, color: props.bgColor, followTheme: false, linear: '' }, isDark.value, tmcfg.value).backgroundColor
	)
	const txtcolor = tool.getColor(props.color).value
	const darkcolor = tmcomputed.value.backgroundColor
	const activeWidth = computed(() => {
		let pr = props.percent >= 100 ? 100 : props.percent
		let w = Math.floor((pr / 100) * props.width)
		w = w >= props.width ? props.width : w
		return w
	})
	const percent_rp = computed(() => {
		let pr = props.percent >= 100 ? 100 : props.percent
		return pr
	})
	watch(
		() => props.percent,
		(newval, oldval) => {
			if (props.disabled) return
			tween = new TWEEN.Tween({ o: oldval })
				.to({ o: newval }, 240)
				.easing(TWEEN.Easing.Linear.None)
				.onUpdate((e: any) => {
					draw(e.o)
				})
				.start()
			emits('update:percent', newval)
			emits('change', newval)
		}
	)
	onBeforeMount(() => {
		clearTimeout(reqId)
		reqFun()
	})
	onMounted(() => {
		let delay = 10
		// #ifdef APP-NVUE
		if (isAndroid) {
			delay = 250
		} else {
			delay = 100
		}
		// #endif
		// #ifdef MP
		delay = 60
		// #endif
		// #ifdef APP-VUE
		delay = 30
		// #endif
		// #ifdef APP-NVUE
		setTimeout(function () {
			showGc.value = true
			setTimeout(() => {
				getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
					.then((e) => {
						ctx = e.ctx
						tween = new TWEEN.Tween({ o: 0 })
							.to({ o: props.percent }, 240)
							.easing(TWEEN.Easing.Linear.None)
							.onUpdate((e: any) => {
								draw(e.o)
							})
							.start()
					})
					.catch((error) => {
						console.error(error)
					})
			}, delay)
		}, 200)
		// #endif

		// #ifndef APP-NVUE
		setTimeout(() => {
			getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
				.then((e) => {
					ctx = e.ctx
					tween = new TWEEN.Tween({ o: 0 })
						.to({ o: props.percent }, 240)
						.easing(TWEEN.Easing.Linear.None)
						.onUpdate((e: any) => {
							draw(e.o)
						})
						.start()
				})
				.catch((error) => {
					console.error(error)
				})
		}, delay)
		// #endif
	})
	onUnmounted(() => uni.$tm.u.cancelAnimationFrame(reqId))
	function reqFun() {
		TWEEN.update()
		reqId = uni.$tm.u.requestAnimationFrame(reqFun)
	}

	function draw(bl: number = 0) {
		if (!ctx) return
		let c: any = tmcomputed.value
		const width = uni.upx2px(props.width)
		let x = width / 2
		let y = width / 2
		const borderWidth = uni.upx2px(props.height)
		const radius = x - borderWidth
		const Pi = Math.PI / 180
		let startAngle = -Pi * 90
		let endAngle = Math.PI * 2
		if (props.semicircle) {
			startAngle = -Pi * 90 * 2
			endAngle = 0
			if (props.semicircleFlip) {
				startAngle = Pi * 180
				endAngle = 0
			}
		}
		//圆环背景色
		let bgColor = _bgColor.value || '#f5f5f5'
		//圆环进度条的颜色
		let activeColor = tool.getColor(props.color).csscolor || '#ff0000'

		ctx.clearRect(0, 0, width, width)
		//先绘制背景圆;
		ctx.lineWidth = borderWidth
		ctx.strokeStyle = bgColor
		ctx.lineCap = 'round'
		ctx.beginPath()
		if (props.semicircle) {
			// 半圆
			if (props.semicircleFlip) {
				y = borderWidth / 2
				ctx.arc(x, y, radius, 0, Pi * 180, false)
			} else {
				y = y - borderWidth / 2
				ctx.arc(x, y, radius, Pi * 180, 0, false)
			}
		} else {
			ctx.arc(x, y, radius, -Math.PI / 2, 1.5 * Math.PI)
		}
		ctx.stroke()

		// 绘制圆形进度环
		const progress = Number((bl / 100).toFixed(6)) // 进度比例，0-1之间的数值
		ctx.beginPath()
		if (props.semicircle) {
			// 半圆
			if (props.semicircleFlip) {
				ctx.arc(x, y, radius, startAngle * (1 - progress), startAngle, false)
			} else {
				ctx.arc(x, y, radius, startAngle, startAngle * (1 - progress), false)
			}
		} else {
			if (props.semicircleFlip) {
				ctx.arc(x, y, radius, startAngle + endAngle * (1 - progress), startAngle + endAngle, false)
			} else {
				ctx.arc(x, y, radius, startAngle, startAngle + endAngle * progress, false)
			}
		}
		if (ctx.setLineWidth) {
			ctx.setLineWidth(borderWidth)
		} else {
			ctx.lineWidth = borderWidth
		}

		// #ifndef APP-NVUE
		//如果是渐变
		if (props.linear) {
			let gradient: any = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y * 2 - borderWidth)
			if (props.semicircle) {
				gradient = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y)
			}
			gradient.addColorStop(0, c.gradientColor[0])
			gradient.addColorStop(1, c.gradientColor[1])
			// ctx.setStrokeStyle(gradient)
			if (ctx.setStrokeStyle) {
				ctx.setStrokeStyle(gradient)
				ctx.setShadow(c.gradientColor[0])
			} else {
				ctx.strokeStyle = gradient
				ctx.shadowColor = c.gradientColor[0]
			}
		} else {
			if (ctx.setStrokeStyle) {
				ctx.setStrokeStyle(activeColor)
				ctx.setShadow(activeColor)
			} else {
				ctx.strokeStyle = activeColor
				ctx.shadowColor = activeColor
			}
		}

		// #endif
		// #ifdef APP-NVUE
		if (ctx.setStrokeStyle) {
			ctx.setStrokeStyle(activeColor)
		} else {
			ctx.strokeStyle = activeColor
		}
		// #endif

		ctx.stroke()
		if (ctx?.draw) {
			ctx.draw()
		}
	}
</script>
<style>
	.tmRogress {
		transition-duration: 0.34s;
		transition-timing-function: linear;
		transition-property: width;
	}

	/* #ifndef APP-NVUE */
	cover-view {
		background: transparent;
	}

	/* #endif */
</style>
